package org.bookshare.document.reader;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;
import org.benetech.exception.WrappedException;
import org.benetech.util.ReflectionUtils;
import org.bookshare.document.beans.DocumentType;

/**
 * Registry mapping readers to document types. Dynamically picks up new reader implementations.
 * @author Reuben Firmin
 */
public final class ReaderRegistry {

	private static ReaderRegistry INSTANCE;
	private Map<DocumentType, Reader> readers = new HashMap<DocumentType, Reader>();

	/**
	 * Private constructor which initializes the registry.
	 */
	private ReaderRegistry() {
		try {
			initialize();
		} catch (ClassNotFoundException e) {
			throw new WrappedException(e);
		} catch (InstantiationException e) {
			throw new WrappedException(e);
		} catch (IllegalAccessException e) {
			throw new WrappedException(e);
		} catch (IOException e) {
			throw new WrappedException(e);
		}
	}

	/**
	 * Get the instance of the converter registry.
	 * @return Never null
	 */
	public static ReaderRegistry getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new ReaderRegistry();
		}
		return INSTANCE;
	}

	/**
	 * Finds all of the readers in the same package as this class, and registers them against their type.
	 * @throws ClassNotFoundException If reflection logic goes wrong
	 * @throws InstantiationException If reflection logic goes wrong
	 * @throws IllegalAccessException If reflection logic goes wrong
	 * @throws IOException If the package couldn't be found on the file path
	 */
	private void initialize()
		throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException
	{
		final Package pkg = this.getClass().getPackage();
		final String pkgName = pkg.getName();
		final Class[] classes = ReflectionUtils.getClassesInPackage(pkgName);
		for (Class clazz : classes) {
			if (ArrayUtils.contains(clazz.getInterfaces(), Reader.class)
					&& !Modifier.isAbstract(clazz.getModifiers()))
			{
				final Reader reader = (Reader) clazz.newInstance();
				final DocumentType supportedType = reader.getDocumentType();
				readers.put(supportedType, reader);
			}
		}
	}

	/**
	 * Get a converter that maps between two given types.
	 * @param type The document type
	 * @return Null if the mapping couldn't be found
	 */
	public Reader getReader(final DocumentType type) {
		return readers.get(type);
	}
}


